/**
============================================================================
 File Name    : touch.c
 Author       : findstr
 Email        : findstr@sina.com
 Description  : (C)  findstr
               @resistor touch pad driver
 Edit History : 2013/09/20 File changed.
 Rev          : 0.1
============================================================================
*/
#include "stm32f10x.h"
#include "trace.h"
#include "stdlib.h"
#include "math.h"
#include "color.h"
#include "lcd.h"
#include "-gui.h"
#include "spi.h"
#include "touch.h"

#define	SW_SPI	1

static struct {
	float 	x_fac;
	float 	y_fac;
	s16 	x_off;
	s16 	y_off;
} touch_param;

#define	SCK_L	GPIO_ResetBits(GPIOA, GPIO_Pin_5)
#define	SCK_H	GPIO_SetBits(GPIOA, GPIO_Pin_5)

#define	MO_L	GPIO_ResetBits(GPIOA, GPIO_Pin_7)
#define	MO_H	GPIO_SetBits(GPIOA, GPIO_Pin_7)

#define	CS_L	GPIO_ResetBits(GPIOB, GPIO_Pin_5)
#define	CS_H	GPIO_SetBits(GPIOB, GPIO_Pin_5)

#define	MI()	GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)

#if	SW_SPI
void sw_spi_init()
{
	GPIO_InitTypeDef	GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5  | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	/* cs */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	/* cs */
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
}


static void sw_spi_write(u8 nr)
{
	u8 i;
	for (i = 0; i < 8; i++) {
		if (nr & 0x80)
			MO_H;
		else
			MO_L;
		nr <<= 1;
		SCK_L;
		SCK_H;
	}
}
void sw_spi_assert()
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_5);
}
void sw_spi_deassert()
{
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
}

#endif

void touch_init()
{
	GPIO_InitTypeDef	GPIO_InitStruct;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

	/* interrupt */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	#if SW_SPI
		sw_spi_init();
	#else
		spi1_init();
	#endif
	
}

u8 touch_pressed()
{
	static u8 last_status = 1;
	u8 status;
	u8 res;
	status = GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_3);
	
	res = last_status & (status ^ last_status);
	last_status = status;
	
	return res;
}

#define	READ_CHX	0x90
#define	READ_CHY	0xd0
static u16 readxy(u8 cmd)
{
	u8 i;
	u16 val;

#if	SW_SPI
	SCK_L;
	MO_L;
	sw_spi_assert();
	sw_spi_write(cmd);
	delay_us(6);
	SCK_L;
	delay_us(1);
	SCK_H;
	SCK_L;
		
	val = 0;
	for (i = 0; i < 16; i++) {
		val <<= 1;
		SCK_L;
		SCK_H;
		if (MI())
			val |= 0x01;
	}
	
	val >>= 4;
	sw_spi_deassert();
#else
	spi1_assert();
	
	spi_write(SPI1, cmd);
	delay_us(6);
	
	val = spi_read(SPI1);
	val <<= 8;
	val |= spi_read(SPI1);
	
	val >>= 3;
	spi1_deassert();
#endif	
	return val;
}
static u32 readpos(u32 *x, u32 *y)
{
	const u32 count = 9;
	u8 i;
	u32 maxx, minx, maxy, miny, tmpx, tmpy;
	u32 sumx, sumy;
	
	sumx = 0;
	maxx = 0;
	minx = 0xffff;
	
	sumy = 0;
	maxy = 0;
	miny = 0xffff;
	for (i = 0; i < count; i++) {
		tmpx = readxy(READ_CHX);
		tmpy = readxy(READ_CHY);
		sumx += tmpx;
		sumy += tmpy;
		
		if (maxx < tmpx)
			maxx = tmpx;
		if (minx > tmpx)
			minx = tmpx;
		
		if (maxy < tmpy)
			maxy = tmpy;
		if (miny > tmpy)
			miny = tmpy;
	}
	sumx -= minx;
	sumx -= maxx;
	*x = sumx / (count - 2);
	
	sumy -= miny;
	sumy -= maxy;
	*y = sumy / (count - 2);

	return 0;
}
void touch_adjust(color_t default_color)
{
	struct {
		struct point phy_pt;
		struct point log_pt;
	} x[4];
	struct rect rt;
	struct point ct;
	u32 i;
	u32 d1, d2;
	u32 tmp1, tmp2;
	float fac;

	x[0].log_pt = (struct point) {
		20, 20
	};
	x[1].log_pt = (struct point) {
		300, 20
	};
	x[2].log_pt = (struct point) {
		20, 200
	};
	x[3].log_pt = (struct point) {
		300, 200
	};
begin:
	for (i = 0; i < sizeof(x) / sizeof(x[0]); i++) {
		rt.start_pt.x = x[i].log_pt.x - 16;
		rt.start_pt.y = x[i].log_pt.y - 16;
		rt.end_pt.x = x[i].log_pt.x + 16;
		rt.end_pt.y = x[i].log_pt.y + 16;
		ct = x[i].log_pt;
		lcd_draw_circle(&ct, 8, COLOR_RED, DRAW_OUTLINE);
		lcd_draw_cross(&rt, COLOR_RED);
		while (!touch_pressed())
			;
		readpos(&x[i].phy_pt.x, &x[i].phy_pt.y);
		//delay_ms(10);
	}
	/* judge the point is valid */
	/* point 1 --> point 2 */
	tmp1 = abs(x[0].phy_pt.x - x[1].phy_pt.x);
	tmp2 = abs(x[0].phy_pt.y - x[1].phy_pt.y);
	tmp1 *= tmp1;
	tmp2 *= tmp2;
	d1 = sqrt(tmp1 + tmp2);
	/* point 3 --> point4 */
	tmp1 = abs(x[2].phy_pt.x - x[3].phy_pt.x);
	tmp2 = abs(x[3].phy_pt.y - x[3].phy_pt.y);
	tmp1 *= tmp1;
	tmp2 *= tmp2;
	d2 = sqrt(tmp1 + tmp2);
	
	fac = (float)d1 / d2;
	if (fac < 0.95 || fac > 1.05) {
		/* adjust error */
		print_d("ajdust error");
		lcd_fresh(default_color);
		goto begin;
	}

	/* point 1 --> point 3 */
	tmp1 = abs(x[0].phy_pt.x - x[2].phy_pt.x);
	tmp2 = abs(x[0].phy_pt.y - x[2].phy_pt.y);
	tmp1 *= tmp1;
	tmp2 *= tmp2;
	d1 = sqrt(tmp1 + tmp2);
	/* point2 --> point 4 */
	tmp1 = abs(x[1].phy_pt.x - x[3].phy_pt.x);
	tmp2 = abs(x[1].phy_pt.y - x[3].phy_pt.y);
	tmp1 *= tmp1;
	tmp2 *= tmp2;
	d2 = sqrt(tmp1 + tmp2);

	fac = (float)d1 / d2;
	if (fac < 0.95 || fac > 1.05) {
		print_d("adjust error");
		lcd_fresh(default_color);
		goto begin;
	}
	
	/*point 1 --> point4 */
	tmp1 = abs(x[0].phy_pt.x - x[3].phy_pt.x);
	tmp2 = abs(x[0].phy_pt.y - x[3].phy_pt.y);
	tmp1 *= tmp1;
	tmp2 *= tmp2;
	d1 = sqrt(tmp1 + tmp2);
	/* point 2 --> point3 */
	tmp1 = abs(x[1].phy_pt.x - x[2].phy_pt.x);
	tmp2 = abs(x[1].phy_pt.y - x[2].phy_pt.y);
	tmp1 *= tmp1;
	tmp2 *= tmp2;
	d2 = sqrt(tmp1 + tmp2);

	fac = (float)d1 / d2;
	if (fac < 0.95 || fac > 1.05) {
		print_d("adust error");
		lcd_fresh(default_color);
		goto begin;
	}
	/* cal x*/
	touch_param.x_fac = (float)(x[1].log_pt.x - x[0].log_pt.x) / (x[1].phy_pt.x - x[0].phy_pt.x);
	touch_param.x_off = (x[1].log_pt.x + x[0].log_pt.x - touch_param.x_fac * (x[1].phy_pt.x + x[0].phy_pt.x)) / 2;
	/* cal y*/
	fac = x[2].log_pt.y - x[0].log_pt.y;
	fac = x[2].phy_pt.y - x[0].phy_pt.y;
	touch_param.y_fac = (float)(x[2].log_pt.y - x[0].log_pt.y) / (x[2].phy_pt.y - x[0].phy_pt.y);
	touch_param.y_off = (x[2].log_pt.y + x[0].log_pt.y - touch_param.y_fac * (x[2].phy_pt.y + x[0].phy_pt.y)) / 2;
	
	/* echo */
	for (i = 0; i < sizeof(x) / sizeof(x[0]); i++) {
		print_d("--logic pos x:%u, y:%u --physic pos x:%u, y:%u\r\n",
			x[i].log_pt.x, x[i].log_pt.y, x[i].phy_pt.x, x[i].phy_pt.y);
	}
	return ;
}

u32 touch_getpos(u16 *x, u16 *y)
{
	u32 tmp_x, tmp_y;
	readpos(&tmp_x , &tmp_y);
	*x = tmp_x * touch_param.x_fac + touch_param.x_off;
	*y = tmp_y * touch_param.y_fac + touch_param.y_off;
	return 0;
}


void touch()
{

}
